<?php

declare(strict_types=1);

namespace app\model;

use PDO;
// use app\core\DB\Db;
use app\core\DB\Test\Db;
use app\core\Log;
use PDOException;
use app\core\Request;
class BaseModel
{
    public $db=null;
    private $strWhere = '';
    private $prefix = 'order_';
    private $create_time = 'create_time';
    private $update_time = 'update_time';
    private $field = '*';
    private $order = '';
    private $group = '';
    private $table = '';
    private $params = [];
    private $limit = '';
    private $lock = '';

    public function __construct()
    {
        // $this->db=Db::getInstance();
      
        $this->db=Request::getDb();
       
        
    }

    private function getField(): string
    {
        return $this->field;
    }
    private function getTable(): string
    {
        return $this->table;
    }
    private function getOrder(): string
    {
        return $this->order;
    }
    private function getGroup(): string
    {
        return $this->group;
    }
    private function getWhere(): string
    {
        return $this->strWhere;
    }
    private function getParams(): array
    {
        return $this->params;
    }
    public function setParams(array $params)
    {
        $this->params = $params;
    }
    public function setField(string $field = '*')
    {
        $this->field = $field;
    }

    public function setTable()
    {
        $className = get_class($this);
        $class_names = array_reverse(explode('\\', $className))[0];
        preg_match_all('/[A-Z|a-z]{0,}/', $class_names, $m);
        $table = strtolower(preg_replace('/(?<=[a-z])(?=[A-Z])/', '_', $m[0][0]));
        $prefix = $this->prefix;
        $this->table = $prefix . $table;

    }
    public function setOrder(string $order)
    {
        $this->order = $order;
    }
    public function setGroup(string $group)
    {
        $this->group = $group;
    }
    public function setWhere(array $where)
    {
        $key = null;
        $operater = null;
        $value = null;
        $params = [];
        $str_where = '';

        foreach ($where as $k => $v) {

            $key = $v[0];
            $operater = strtoupper($v[1]);
            $valueKey = ":$key";
            $value = $v[2];

            switch ($operater) {
                case 'IN':
                case 'NOT IN':
                    if (is_array($value)) {
                        $value = implode(',', $value);
                    }
                    $value = "($value)";
                    break;
                case 'REGEXP':
                    $value = "'" . trim($value) . "'";
                    break;
                case 'BETWEEN':
                    $value = preg_replace("/,/", ' AND ', $value);
                    break;
                default:
                    $params[$valueKey] = $value;
                    $value = $valueKey;
                    break;
            }

            $str_where .= $key . ' ' . $operater . ' ' . $value . ' AND ';
        }
        $where = rtrim($str_where, ' AND');
        $this->setParams($params);
        $this->strWhere = $where;
    }

    public function where(array $where)
    {
        $this->setWhere($where);
        return $this;
    }
    /**
     * 排序
     *
     * @param string $order
     * @return self
     */
    public function order(string $order)
    {
        if (!empty($order)) {
            $order = ' ORDER BY ' . $order . ' ';
        }
        $this->setOrder($order);
        return $this;
    }
    /**
     * 以某字段为一组
     *
     * @param string $group
     * @return self
     */
    public function group(string $group)
    {
        if (!empty($group)) {
            $group = ' GROUP BY ' . $group . ' ';
        }
        $this->setGroup($group);
        return $this;
    }
    public function field(string $field = '*')
    {
        if ($field != '*') {
            $field = explode(',', $field);
            $field = implode(',', $field);
        }
        $this->setField($field);

        return $this;
    }
    public function getLimit()
    {
        return $this->limit;
    }
    public function limit(int $page_num = 1, int $row = 5)
    {
        $start = ($page_num - 1) * $row;
        $this->limit = "LIMIT $start,$row";
        return $this;
    }
    public function setLock(string $lock){
        $this->lock = $lock;
    }
    public function lock(int $type = 1)
    {
        $lock = '';
        switch ($type) {
            case 1:
                $lock = 'FOR UPDATE';
                break;
            case 2:
                $lock = 'LOCK IN SHARE MODE';
                break;

            default:
                $lock = '';
                break;
      
              
        }
        $this->setLock($lock);
        return $this;
    }

    public function getLock()
    {
        return  $this->lock;
    }
    /**
     * 查询
     *
     * @return array
     */
    public function select()
    {
      
            $this->setTable();
            $field = $this->getField();
            $table = $this->getTable();
            $group = $this->getGroup();
            $order = $this->getOrder();
            $where = $this->getWhere();
            $limit = $this->getLimit();
            $lock = $this->getLock();
            // $sql = 'SELECT ' . $field . ' FROM ' . $table;


            if (!empty($where)) {
                $where = ' WHERE ' . $where;
            }
            // $sql = $where . ' ' . $group . ' ' . $order . ' ' . $limit.' ' . $lock.';';
            $sql ="SELECT $field FROM $table $where $group $order $limit $lock";
            // $prepare = Db::prepare($sql);
            $params = $this->getParams();
            // foreach ($params as $k => $v) {
            //     $mapValue = $this->typeMap($v, gettype($v));
            //     $prepare->bindValue($k, $mapValue[0], $mapValue[1]);
            // }
            // $prepare->execute();
            // $res = $prepare->fetchAll();
            // $prepare->closeCursor();
            // Log::error($sql);
            $res=$this->db->query($sql,$params);
            $this->setField('*');
            $this->setLock('');
          
            return $res;
    
    }
    /**
     * 更新
     *
     * @param array $data
     * @param array $where
     * @return integer
     */
    public function update(array $data, array $where = [])
    {
       
            if (!empty($where)) {
                $this->setWhere($where);
            }
            $this->setTable();


            $table = $this->getTable();
            $lock = $this->getLock();
            $sql='';
            $updateColumns = [];
            $data[$this->update_time] = $this->setTime();
            foreach ($data as $k => $v) {
                $value = is_string($v) ? "'$v'" : $v;

                $updateColumns []= "`$k`=$value";
            }


            $updateColumns = implode(',',$updateColumns);
           
            if (!empty($where)) {
                $this->setWhere($where);
                $where = $this->getWhere();
            }else{
                $where = $this->getWhere();
               
            }
            $sql = "UPDATE $table SET $updateColumns WHERE $where";
         

            // Log::error($sql);
            $params = $this->getParams();
              
            $rowCount=$this->db->execute($sql,$params);
          
            return $rowCount;
       
    }
    /**
     * 保存，并返回保存后的主键id
     *
     * @param array $data
     * @return string|false
     */
    public function save(array $data)
    {
      
            $this->setTable();
            $time = $this->setTime();
            $table = $this->getTable();
    
    
            $sql = '';
    
            $columns =[];
            $values = [];
            $params = [];
            $data[$this->create_time] = $time;
            $data[$this->update_time] = $time;
            foreach ($data as $k => $v) {
                $columns []="`$k`";
                $key = ":$k";
                $values []= $key;
                $params[$key] = $v;
            }
            $values = implode(',',$values);
            $columns = implode(',',$columns);
            $sql = "INSERT INTO $table ($columns) VALUES ($values);";
            $lastInsertId=$this->db->insert($sql,$params);
            return $lastInsertId;
     

     
    }
    /**
     * 单个查询
     *
     * @return array
     */
    public function find()
    {
       
        // Log::error('db:'.$this->db);
            $sql = '';
            $this->setTable();
            $table = $this->getTable();
            $field = $this->getField();
            $where = $this->getWhere();
            $lock=$this->getLock();
            if (!empty($where)) {
                $where = "WHERE $where" ;
            }
    
            
            $sql = "SELECT $field FROM $table $where $lock;";
           
            // $prepare = Db::prepare($sql);
    
            $params = $this->getParams();
            // Log::error($sql);
            // Log::error($this->db);
            $fetch=$this->db->fetch($sql,$params);
            // foreach ($params as $k => $v) {
            //     $mapValue = $this->typeMap($v, gettype($v));
            //     $prepare->bindValue($k, $mapValue[0], $mapValue[1]);
            // }
            // $prepare->execute();
            // $fetch = $prepare->fetch();
            // $prepare->closeCursor();
            // Log::error($sql);
            $this->setField('*');
            $this->setLock('');
            // Log::info($sql);
            return empty($fetch) ? [] : $fetch;
       
      
    }

    protected function typeMap($value, $type)
    {
        $map = [
            'NULL' => PDO::PARAM_NULL,
            'integer' => PDO::PARAM_INT,
            'double' => PDO::PARAM_STR,
            'boolean' => PDO::PARAM_BOOL,
            'string' => PDO::PARAM_STR,
            'object' => PDO::PARAM_STR,
            'resource' => PDO::PARAM_LOB,
        ];

        if ($type === 'boolean') {
            $value = ($value ? '1' : '0');
        } elseif ($type === 'NULL') {
            $value = null;
        }

        return [$value, $map[$type]];
    }

    public function saveAll(array $data)
    {
       
       
            if (empty($data)) {
                return false;
            }
            $this->setTable();
            $table = $this->getTable();
            $time = $this->setTime();
            $createTimePreposition  =":$this->create_time";
            $updateTimePreposition  =":$this->update_time";
           
            $createTime="`$this->create_time`";
            $updateTime="`$this->update_time`";
            $timeColumnSet=[$createTime,$updateTime];
            $timeColumnPrepositionSet=[$createTimePreposition,$updateTimePreposition];
            $statements = [];
            for ($i = 0; $i < count($data); $i++) {
    
                $columns = [];
                $values = [];
    
                $statements[$i] = [
                    'sql' => '',
                    'data' => []
                ];
                foreach ($data[$i] as $k1 => $v1) {
                    $columns[]= "`$k1`";
                    $key = ":$k1";
                    $statements[$i]['data'][$key] = $v1;
    
                    $values[]= $key;
                }
                
                $statements[$i]['data'][$createTimePreposition] = $time;
                $statements[$i]['data'][$updateTimePreposition] = $time;
                $columns = implode(',',array_merge($columns,$timeColumnSet));
                $values = implode(',',array_merge($values,$timeColumnPrepositionSet));
              
                $statements[$i]['sql'] = "INSERT INTO $table ($columns) VALUES ($values);";
            }

           
        
          
            foreach ($statements as $k => $v) {
                $prepare = $this->db->insert($v['sql'],$v['data']);
            }
    
    
     
      
    }


    public function setTime()
    {
        return time();
    }
}
